package preprocess;

import java.util.HashMap;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.Text;
import org.dom4j.io.SAXReader;

import ui.xmlVisualization.XMLTree;

import static preprocess.XMLContracter.*;

/**
 * a wrapper for the xml document
 * 
 * @author huangcd
 * 
 */
@SuppressWarnings("unused")
public class XMLDoc
{
    private Document               doc;
    private String                 docID;

    // store the level information of the nodes, the level of the root element
    // is 0
    private HashMap<Node, Integer> levelsMap;
    // store the descendant count of the nodes
    private HashMap<Node, Integer> descendantCount;
    //
    private HashMap<Node, Integer> positionMap;
    // store all the text node of this document
    // private LinkedList<Text> textSet;
    private String                 text;
    private StringBuilder          textBuffer;
    private double[]               vsm;

    // height of the document, if there is only one root element in this
    // document, the height should be 1
    private int                    docHeight;

    /**
     * 不需要对XML文档做特殊处理的时候，使用本构造函数创建XMLDoc实例。（然后用setDocID指定DocID）
     * 
     * @param initDoc
     */
    public XMLDoc(Document initDoc)
    {
        this.doc = initDoc;
    }

    public XMLDoc(String initDocID)
    {
        this.docID = initDocID;
    }

    public XMLDoc(Document initDoc, String initDocID)
    {
        this.doc = initDoc;
        this.docID = initDocID;
        getInfo();
    }

    public void getInfo()
    {
        if (doc == null)
            return;
        if (levelsMap == null)
            levelsMap = new HashMap<Node, Integer>();
        if (descendantCount == null)
            descendantCount = new HashMap<Node, Integer>();
        if (positionMap == null)
            positionMap = new HashMap<Node, Integer>();
        // if (textSet == null)
        // textSet = new LinkedList<Text>();
        if (textBuffer == null)
            textBuffer = new StringBuilder();
        Element root = doc.getRootElement();
        docHeight = -1;
        positionMap.put(root, 0);
        getInfo(root, 0);
        docHeight++;
    }

    private int getInfo(Node node, int level)
    {
        docHeight = Math.max(docHeight, level);
        levelsMap.put(node, level);
        int sum = 0;
        if (node instanceof Text)
        {
            sum = 0;
            System.err.println("maybe error occur...");
        }
        else if (node instanceof Element)
        {
            Element elem = (Element) node;
            for (int i = 0; i < elem.nodeCount(); i++)
            {
                Node child = elem.node(i);
                if (child instanceof Text)
                {
                    String str = child.getText().trim();
                    if (!str.isEmpty())
                        textBuffer.append(str).append(' ');
                    // if the current text node contains only whitespace, remove
                    // it from the document
                    else
                    {
                        elem.remove(child);
                        i--;
                    }
                }
                else
                {
                    positionMap.put(child, i);
                    sum += getInfo(child, level + 1) + 1;
                }
            }
        }
        else
        {
            sum = -1;
        }
        descendantCount.put(node, sum);
        return sum;
    }

    public int getPosition(Node node)
    {
        if (positionMap == null)
            return -1;
        if (!positionMap.containsKey(node))
            return -1;
        return positionMap.get(node);
    }

    /**
     * return the number of descendants of the given node, 0 stand for leaf
     * node. -1 while error occurs
     * 
     * @param node
     * @return
     */
    public int getDescendantCount(Node node)
    {
        if (descendantCount == null)
            return -1;
        if (!descendantCount.containsKey(node))
            return -1;
        return descendantCount.get(node);
    }

    /**
     * return the level of the given node, 0 stand for root node. -1 while error
     * occurs
     * 
     * @param node
     * @return
     */
    public int getLevel(Node node)
    {
        if (levelsMap == null)
            return -1;
        if (!levelsMap.containsKey(node))
            return -1;
        return levelsMap.get(node);
    }

    public Document getDoc()
    {
        return this.doc;
    }

    public void setDoc(Document newDoc)
    {
        this.doc = newDoc;
    }

    public String getDocID()
    {
        return this.docID;
    }

    public void setDocID(String newID)
    {
        this.docID = newID;
    }

    public Element getRoot()
    {
        return this.doc.getRootElement();
    }

    /**
     * return all the text of this document, separate by whitespace
     * 
     * @return
     */
    public String getAllText()
    {
        if (text == null)
        {
            // StringBuilder buffer = new StringBuilder();
            // for (Text t : textSet)
            // {
            // buffer.append(t.getText()).append(' ');
            // }
            // text = buffer.toString();
            text = textBuffer.toString();
            // System.gc();
        }
        return text;
    }

    /**
     * @return the height of this document, if there is only one root element in
     *         this document, the height should be 1
     */
    public int getDocHeight()
    {
        return docHeight;
    }

    @Override
    public String toString()
    {
        return docID;
    }

    /**
     * tester and demo for this class
     * 
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception
    {
        SAXReader reader = new SAXReader();
        Document document = reader
        // .read("treeT1.xml");
                .read("file:///D:/INEX Dataset/test set/1474166/1945912.xml");
        // XMLTree.showTree(document);
        XMLContracter dc = new XMLContracter();
        dc.contract(document, ATTRIBUTE | NESTING | REPETITION);
        XMLTree.showTree(document);
        XMLDoc doc = new XMLDoc(document, "1945912");
        System.out.println(doc.levelsMap.values().toString());
        System.out.println(doc.descendantCount.values().toString());
        System.out.println(doc.positionMap.values().toString());
        System.out.println(doc.getDocHeight());
        XMLTree.showTree(doc.getDoc());
    }
}
